package com.voe;

import com.voe.AudioFrame.SpeechType;
import com.voe.AudioFrame.VADActivity;
import com.voe.callback.VoEMediaProcess;
import com.voe.common.OutputParam;
import com.voe.common.ProcessingTypes;
import com.voe.common.ReturnValue;
import com.voe.common.Utility;

/**In some cases it is desirable to use an audio source or sink which may<br/>
*not be available to the VoiceEngine, such as a DV camera. This sub-API<br/>
*contains functions that allow for the use of such external recording<br/>
*sources and playout sinks. It also describes how recorded data, or data<br/>
*to be played out, can be modified outside the VoiceEngine.<br/>
*<br/>
*Usage example, omitting error checking:<br/>
*<br/>
*VoiceEngine voe = VoiceEngine.createInterface();<br/>
*VoEBase base = VoEBase.createInterface(voe);<br/>
*VoEMediaProcess media = VoEMediaProcess.createInterface(voe);<br/>
*base.init();<br/>
*...<br/>
*media.setExternalRecordingStatus(true);<br/>
*...<br/>
*base.terminate();<br/>
*base.release();<br/>
*media.release();<br/>
*voe.release();<br/>
*/
public class VoEExternalMedia {

	// [start] apis

	public static VoEExternalMedia createInstance(VoiceEngine voiceEngine) {

		long p = GetInterface(voiceEngine._p);
		if(p != 0)
			return new VoEExternalMedia(p);
		return null;
	}

	public void release() {
		if (_p != 0) {
			Release(_p);
		}
	}

	/** Installs a VoEMediaProcess derived instance and activates external<br/>
	 *  media for the specified |channel| and |type|.
	 */
	public int registerExternalMediaProcessing(int channel,
			ProcessingTypes type, VoEMediaProcess processObject) {
		Utility.throwNullPointerException(type, "type");
		Utility.throwNullPointerException(processObject, "processObject");
		return RegisterExternalMediaProcessing(_p, channel, type.getValue(),
				processObject);
	}

	/** Removes the VoEMediaProcess derived instance and deactivates external<br/>
	 *  media for the specified |channel| and |type|.
	 */
	public int deRegisterExternalMediaProcessing(int channel,
			ProcessingTypes type) {
		Utility.throwNullPointerException(type, "type");
		return DeRegisterExternalMediaProcessing(_p, channel, type.getValue());
	}

	/** Toogles state of external recording.
	 */
	public int setExternalRecordingStatus(boolean enable) {
		return SetExternalRecordingStatus(_p, enable);
	}

	/** Toogles state of external playout.
	 */
	public int setExternalPlayoutStatus(boolean enable) {
		return SetExternalPlayoutStatus(_p, enable);
	}

	/** This function accepts externally recorded audio. During transmission,<br/>
	 *  this method should be called at as regular an interval as possible<br/>
	 *  with frames of corresponding size.
	 */
	public int externalRecordingInsertData(short[] speechData10ms,
			int samplingFreqHz, int current_delay_ms) {
		return ExternalRecordingInsertData(_p, speechData10ms, samplingFreqHz,
				current_delay_ms);
	}

	/** This function gets audio for an external playout sink.<br/>
	 *  During transmission, this function should be called every ~10 ms<br/>
	 *  to obtain a new 10 ms frame of audio. The length of the block will<br/>
	 *  be 160, 320, 440 or 480 samples (for 16, 32, 44 or 48 kHz sampling<br/>
	 *  rates respectively).
	 */
	public int externalPlayoutGetData(short[] speechData10ms,
			int samplingFreqHz, int current_delay_ms,
			ReturnValue<Integer> lengthSamples) {
		OutputParam output = new OutputParam();
		int result = ExternalPlayoutGetData(_p, speechData10ms, samplingFreqHz,
				current_delay_ms, output);
		if(result == 0) {
			lengthSamples.setValue((Integer) output.getObject()[0]);
		}
		return result;
	}

	/** Pulls an audio frame from the specified |channel| for external mixing.<br/>
	 *  If the |desired_sample_rate_hz| is 0, the signal will be returned with<br/>
	 *  its native frequency, otherwise it will be resampled. Valid frequencies<br/>
	 *  are 16, 22, 32, 44 or 48 kHz.
	 */
	public int getAudioFrame(int channel, int desired_sample_rate_hz,
			ReturnValue<AudioFrame> frame) {
		OutputParam output = new OutputParam();
		int result = GetAudioFrame(_p, channel, desired_sample_rate_hz, output);
		if(result == 0) {
			AudioFrame tmp = new AudioFrame();
			tmp.id_ = (Integer) output.getObject()[0];
			tmp.timestamp_ = (Integer) output.getObject()[1];
			tmp.data_ = (short[]) output.getObject()[2];
			tmp.samples_per_channel_ = (Integer) output.getObject()[3];
			tmp.sample_rate_hz_ = (Integer) output.getObject()[4];
			tmp.num_channels_ = (Integer) output.getObject()[5];
			tmp.speech_type_ = SpeechType
					.fromValue((Integer) output.getObject()[6]);
			tmp.vad_activity_ = VADActivity
					.fromValue((Integer) output.getObject()[7]);
			tmp.energy_ = (Integer) output.getObject()[8];
			frame.setValue(tmp);
		}
		return result;
	}

	/** Sets the state of external mixing. Cannot be changed during playback.
	 */
	public int setExternalMixing(int channel, boolean enable) {
		return SetExternalMixing(_p, channel, enable);
	}

	protected VoEExternalMedia(long p) {
		_p = p;
	}

	protected long _p;

	// [end]

	// [start] jni api

	// Factory for the VoEExternalMedia sub-API. Increases an internal
	// reference counter if successful. Returns NULL if the API is not
	// supported or if construction fails.
	private static native long GetInterface(long p_voiceEngine);

	// Releases the VoEExternalMedia sub-API and decreases an internal
	// reference counter. Returns the new reference count. This value should
	// be zero for all sub-API:s before the VoiceEngine object can be safely
	// deleted.
	private static native int Release(long p_VoEExternalMedia);

	// Installs a VoEMediaProcess derived instance and activates external
	// media for the specified |channel| and |type|.
	private static native int RegisterExternalMediaProcessing(
			long p_VoEExternalMedia, int channel, int type,
			VoEMediaProcess processObject);

	// Removes the VoEMediaProcess derived instance and deactivates external
	// media for the specified |channel| and |type|.
	private static native int DeRegisterExternalMediaProcessing(
			long p_VoEExternalMedia, int channel, int type);

	// Toogles state of external recording.
	private static native int SetExternalRecordingStatus(
			long p_VoEExternalMedia, boolean enable);

	// Toogles state of external playout.
	private static native int SetExternalPlayoutStatus(long p_VoEExternalMedia,
			boolean enable);

	// This function accepts externally recorded audio. During transmission,
	// this method should be called at as regular an interval as possible
	// with frames of corresponding size.
	private static native int ExternalRecordingInsertData(
			long p_VoEExternalMedia, short[] speechData10ms,
			int samplingFreqHz, int current_delay_ms);

	// This function gets audio for an external playout sink.
	// During transmission, this function should be called every ~10 ms
	// to obtain a new 10 ms frame of audio. The length of the block will
	// be 160, 320, 440 or 480 samples (for 16, 32, 44 or 48 kHz sampling
	// rates respectively).
	private static native int ExternalPlayoutGetData(long p_VoEExternalMedia,
			short[] speechData10ms, int samplingFreqHz, int current_delay_ms,
			OutputParam lengthSamples);

	// Pulls an audio frame from the specified |channel| for external mixing.
	// If the |desired_sample_rate_hz| is 0, the signal will be returned with
	// its native frequency, otherwise it will be resampled. Valid frequencies
	// are 16, 22, 32, 44 or 48 kHz.
	private static native int GetAudioFrame(long p_VoEExternalMedia,
			int channel, int desired_sample_rate_hz, OutputParam frame);

	// Sets the state of external mixing. Cannot be changed during playback.
	private static native int SetExternalMixing(long p_VoEExternalMedia,
			int channel, boolean enable);

	// [end]
}
